package com.xm.cas;

import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * 版本号标记：
 *   T1 先将引用从 0 设置为 1，然后再将其恢复为 0，并且每次都增加了版本号。
 *   T2 在等待期间错过了中间的修改过程，在最终判断时发现引用仍然是 0，但由于版本号已经发生变化，因此无法成功地将 0 更改为 1。
 */
public class AtomicStampedReference01 {
    public static void main(String[] args) throws InterruptedException {
        Integer initialRef = 0;
        int initialStamp = 0;
        AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(initialRef, initialStamp);

        // 创建线程 T1 修改状态为 1，然后恢复为 0
        Thread t1 = new Thread(() -> {
            try {
                int[] stampHolder = new int[1];
                Integer reference = stampedReference.get(stampHolder);
                int stamp = stampHolder[0];

                // 第一步：修改为 1
                boolean updatedTo1 = stampedReference.compareAndSet(reference, 1, stamp, stamp + 1);
                System.out.println("T1 set to 1? " + updatedTo1);

                // 等待一段时间让主线程也获取到初始状态
                Thread.sleep(100);

                // 第二步：还原为 0
                boolean restoredToInitial = stampedReference.compareAndSet(1, 0, stamp + 1, stamp + 2);
                System.out.println("T1 restore to 0? " + restoredToInitial);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        // 创建线程 T2 监视状态变化
        Thread t2 = new Thread(() -> {
            try {
                int[] stampHolder = new int[1];
                Integer reference = stampedReference.get(stampHolder);
                int stamp = stampHolder[0];

                // 等待一段时间以便 T1 完成分两次修改
                Thread.sleep(200);

                // 尝试将 0 改为 1
                boolean success = stampedReference.compareAndSet(reference, 1, stamp, stamp + 1);
                System.out.println("T2 set from 0 to 1 successful? " + success +
                        ". Current Ref: " + stampedReference.getReference() +
                        ", Stamp: " + stampedReference.getStamp());
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();
    }
}
